#include "OpenglAppFramework/inc/PlugIn/PlugIn.h"
#include "OpenglAppFramework/inc/PlugIn/Camera.h"
#include "OpenglAppFramework/inc/PlugIn/LightSource.h"
#include "OpenglAppFramework/inc/PlugIn/Material.h"
#include "OpenglAppFramework/inc/PlugIn/Mesh.h"
#include "OpenglAppFramework/inc/PlugIn/ActionsPlugIn.h"
#include "OpenglAppFramework/inc/PlugIn/InstancingPlugIn.h"
#include "OpenglAppFramework/inc/PlugIn/LODPlugIn.h"
#include <iostream>

static const std::map<std::string, PlugInType> str_plugin_type_map = {
    {"mesh", PlugInType::Mesh},
    {"material", PlugInType::Material},
    {"camera", PlugInType::Camera},
    {"parallel_light", PlugInType::ParallelLight},
    {"actions", PlugInType::Actions},
    {"instancing", PlugInType::Instancing},
    {"lod", PlugInType::LOD}
};

PlugInType string2PlugInType(const std::string& str) {
    auto it = str_plugin_type_map.find(str);
    if(it != str_plugin_type_map.end()) {
        return it->second;
    }
    return PlugInType::Illegal;
}

PlugIn::PlugIn(const std::string& name, PlugInType type):
    m_name(name),
    m_plugin_type(type) {
    m_is_enable = true;
}

PlugIn::~PlugIn() {}

PlugIn* PlugIn::CreatePlugInByJson(const std::string& name, const nlohmann::json& js) {
    PlugIn* plugin;
    const std::string& type_string = js["plugin_type"];
    PlugInType type = string2PlugInType(type_string);
    switch (type) {
    case PlugInType::Mesh :             plugin = new Mesh(name); break;
    case PlugInType::Material :         plugin = new Material(name); break;
    case PlugInType::Camera :           plugin = new Camera(name); break;
    case PlugInType::ParallelLight :    plugin = new ParallelLightSource(name); break;
    case PlugInType::Actions :          plugin = new ActionsPlugIn(name); break;
    case PlugInType::Instancing :       plugin = new InstancingPlugIn(name); break;
    case PlugInType::LOD :              plugin = new LODPlugIn(name); break;
    default: 
        std::cout << "illeagle plugin type: " << type_string << std::endl;
        return nullptr;
    }

    if(plugin->setConfigData(js["data"]) != 0) {
        delete plugin;
        return nullptr;
    }
    
    return plugin;
}

PlugInType PlugIn::type() const { return m_plugin_type; }

void PlugIn::setGameObject(GameObject* obj) {
    m_obj = obj;
}

void PlugIn::setEnable(bool enable) {
    m_is_enable = enable;
}

const std::string& PlugIn::name() const { return m_name; }

bool PlugIn::isEnable() const { return m_is_enable; }

